package com.xmindguoguo.boot.core.aop;

import java.util.Objects;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.xmindguoguo.boot.common.constant.SessionConstant;
import com.xmindguoguo.boot.core.shiro.ShiroKit;
import com.xmindguoguo.boot.core.shiro.factroy.IShiro;
import com.xmindguoguo.boot.core.shiro.factroy.RememberedSessionUtil;
import com.xmindguoguo.boot.core.shiro.factroy.ShiroFactroy;
import com.xmindguoguo.boot.util.http.HttpUtil;
import com.xmindguoguo.boot.util.json.JsonResult;

import lombok.extern.slf4j.Slf4j;

/**
 * 自定义检查权限 根据 RequestMapping 注解的URL进行检查
 * 
 * @ClassName ShiroAop
 * @author <a href="892042158@qq.com" target="_blank">于国帅</a>
 * @date 2019年2月8日 上午10:40:43
 *
 */
@Aspect
@Component
@Slf4j
public class ShiroAop {

    @Pointcut(value = "@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void cutShiroService() {
    }

    /**
     * 方法级别检查权限，把这个aop当成过滤器的效果，只检查数据库权限表存在的url（需要登录权限和需要登录无须权限的），其他的默认放行
     * 
     * @改动权限需要重新登录才有效果
     * @Title checkShiroPermission
     * @author 于国帅
     * @date 2019年2月8日 下午11:11:03
     * @param point
     * @return
     * @throws Throwable
     *             Object
     */
    @Around("cutShiroService()")
    public Object checkShiroPermission(ProceedingJoinPoint point) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 如果是记住密码 ，则刷新记住密码需要的Session
        if (Objects.isNull(ShiroKit.getSessionAttr(SessionConstant.SHIRO_USER)) || ShiroKit.getSubject().isRemembered()) {
            RememberedSessionUtil.refreshRememberedSession(); // 所有的需要刷新的session数据统一根据Session 来编写？ 暂时统一放在一个类里面吧
        }
        boolean result = checkShiroPermission(request);
        if (result) { // 存在权限正确返回
            return point.proceed();
        } else { // 不存在权限直接返回错误
            if (HttpUtil.isAjax(request)) { // NoPermissionException
                return JsonResult.sendError("未存在指定权限");
            } else {
                return "/common/error/unauthorized";
            }
        }
    }

    /**
     * 检查当前URL是否存在权限
     * 
     * @Title checkShiroPermission
     * @author 于国帅
     * @date 2019年2月28日 下午2:18:17
     * @param request
     * @return boolean
     */
    private boolean checkShiroPermission(HttpServletRequest request) {
        boolean result = true;
        // 获得当前请求URL
        String requestURI = request.getServletPath();
        // 先检查需要登录需要权限的URL
        Set<String> longinAndPerSet = ShiroKit.getSessionAttr(SessionConstant.Permission.LONGIN_AND_PER_SET);
        if (longinAndPerSet == null) {
            IShiro shiroFactory = ShiroFactroy.me();
            longinAndPerSet = shiroFactory.findLoginAndPermissionSet();
            ShiroKit.setSessionAttr(SessionConstant.Permission.LONGIN_AND_PER_SET, longinAndPerSet);
        }
        if (longinAndPerSet != null && longinAndPerSet.contains(requestURI)) {
            result = ShiroKit.hasPermission(requestURI);
        }
        // 然后检查是否登录 // 未登录再检查 需要登录的URL
        if (ShiroKit.getUser() == null) {
            Set<String> longinSet = ShiroKit.getSessionAttr(SessionConstant.Permission.LONGIN_SET);
            if (longinSet == null) {
                IShiro shiroFactory = ShiroFactroy.me();
                longinSet = shiroFactory.findLoginNoPermissionSet();
                ShiroKit.setSessionAttr(SessionConstant.Permission.LONGIN_SET, longinSet);
            }
            if (longinSet != null && longinSet.contains(requestURI)) {
                result = ShiroKit.hasPermission(requestURI);
            }
        }
        return result;
    }

}
